#ifndef DATABASE_LOG_H
#define DATABASE_LOG_H

#include <fstream>
#include <mutex>
#include <sstream>
#include <string>

namespace Database
{

namespace Logging
{

#ifndef LOG_LEVEL
#define LOG_LEVEL (LL_DEBUG)
#endif
#define Log(x) if((x) > LOG_LEVEL) ; else LogWriter(x, __FILE__, __LINE__)

using std::ofstream;
using std::ostringstream;
using std::string;

class LogLevel
{
public:
    LogLevel(const int level, const string& name)
        : level_(level), name_(name) { };

    virtual ~LogLevel() { };

    operator const char *() const { return name_.c_str(); };
    operator const string&() const { return name_; };

    const bool operator>(const LogLevel& rhs) const { return level_ > rhs.level_; };

private:
    int level_;
    const string name_;
};


static LogLevel LL_DONTLOG(-1, "");
static LogLevel LL_ERROR(0, "ERROR");
static LogLevel LL_WARNING(1, "WARNING");
static LogLevel LL_INFO(2, "INFO");
static LogLevel LL_NOTICE(3, "NOTICE");
static LogLevel LL_DEBUG(4, "DEBUG");


class LogWriter
{
public:
    LogWriter() = delete;
    LogWriter(ofstream& ost);
    LogWriter(const LogLevel& level);
    LogWriter(const LogLevel& level, const char * const file, const unsigned int line);
    virtual ~LogWriter();

    template<typename T> LogWriter& operator<<(T const & val)
    {
        buffer_ << val;
        return *this;
    }

    static const bool open(const char * const fileName);
    static const bool close();

private:
    const string getPidStr() const;
    const string getTimeStr() const;

    static ofstream *ost_;
    ostringstream buffer_;
    static std::mutex mutexWrite_;
};

}   // namespace Logging

}   // namespace Database

#endif // LOG_H
